﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.IO;
using System.Text;

using Data;
using EuphoricNation.Page;
using EuphoricNation.Util;

namespace EuphoricNation.Rewrite
{
    /// <summary>
    /// Perform URL rewriting
    /// Based on: http://www.simple-talk.com/dotnet/asp.net/a-complete-url-rewriting-solution-for-asp.net-2.0/
    /// </summary>
    public class RewriteModule : IHttpModule
    {
        private readonly string[] Whitelist = new string[] { "App_Themes", "media", "NeatUpload", "services", "ckeditor", "Admin" };

        public void Dispose() { }

        public bool IsReusable { get { return false; } }

        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(context_BeginRequest);
            context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
        }

        /// <summary>
        /// Format the input path to get the actual request path, handles 404s properly
        /// </summary>
        /// <param name="request">Current request</param>
        /// <returns>Request path</returns>
        private string GetPath(HttpRequest request)
        {
            string path = request.Path;

            try
            {
                if (path == "/404.aspx")
                {
                    path = request.RawUrl.Split(';')[1];

                    // Remove the domain
                    int pos = path.IndexOf('/', 8);
                    path = path.Substring(pos);

                    // Remove query variables
                    pos = path.IndexOf('?');
                    if (pos > 0)
                        path = path.Substring(0, pos);

                    path += "Default.aspx";
                }
            }
            catch (IndexOutOfRangeException)
            {
                return request.Path;
            }
            catch (ArgumentOutOfRangeException)
            {
                return request.Path;
            }

            return path;
        }

        /// <summary>
        /// Get the variables for a route
        /// </summary>
        /// <param name="route">Route to get variables for</param>
        /// <param name="path">Path to page</param>
        /// <returns>URL-encoded variables</returns>
        private string GetRouteVariables(HttpApplication app, Route route, string path)
        {
            HttpServerUtility Server = app.Server;
            string[] parts = path.Replace("Default", "").Split('/').TrimBlank();
            StringBuilder output = new StringBuilder("?");
            Dictionary<string, string> vars = route.GetVariables(ref parts);

            if (vars.Count == 0)
                return string.Empty;

            app.Context.Items[Constants.KEY_SHAREDCONTEXT_GETVARS] = vars;

            foreach (KeyValuePair<string, string> pair in vars)
                output.AppendFormat("{0}={1}&", Server.UrlEncode(pair.Key), Server.UrlEncode(pair.Value));

            return output.ToString();
        }

        protected void context_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;
            HttpRequest Request = app.Request;
            string path = GetPath(Request);

            // Skip blank paths
            if (path.Length == 0)
                return;

            string[] pathSplit = path.Split('/').TrimBlank();

            // Handle redirects in the first instance
            Redirect redirect = Redirects.Current.GetRedirect(path);
            if (redirect != null)
            {
                if (redirect.Permanent)
                    Useful.RedirectPermanent(redirect.ToURL);
                else
                    Useful.RedirectTemporary(redirect.ToURL);

                return;
            }

            string ext = Path.GetExtension(path);

            // Skip non-aspx and non-web page content
            if (ext != ".aspx" && ext.Length > 0)
                return;
            else if (ext.Length == 0 && !path.EndsWith("/"))
                path = path + "/";

            // Skip Whitelist stuff
            if (pathSplit.Length > 0)
            {
                foreach (string white in Whitelist)
                {
                    if (pathSplit[0].Equals(white, StringComparison.CurrentCultureIgnoreCase))
                        return;
                }
            }

            // Replace .aspx with /
            if (ext == ".aspx" && !path.EndsWith("default.aspx", StringComparison.CurrentCultureIgnoreCase) && !path.ToLower().Contains("admin") && !path.ToLower().Contains("login"))
            {
                path = path.Replace(".aspx", "/");
                Useful.RedirectPermanent(path);
                
                return;
            }

            // Check if we have a direct filename replacement
            try
            {
                int lastSlashPos = path.LastIndexOf('/');
                if (lastSlashPos > 0)
                {
                    string filename = string.Format("{0}.aspx", path.Substring(0, lastSlashPos));
                    if (File.Exists(app.Server.MapPath(filename)))
                    {
                        app.Context.Items.Add("OriginalUrl", path);
                        app.Context.RewritePath(filename);

                        return;
                    }
                }
            }
            catch (ArgumentException)
            {
            }
            catch (InvalidOperationException)
            {
            }

            // Check if the request is a page in the CMS
            string cmsPath = PageInCMS(app, path);
            if (!string.IsNullOrEmpty(cmsPath))
            {
                app.Context.Items.Add("OriginalUrl", path);
                app.Context.RewritePath(cmsPath);

                return;
            }

            // Handle search
            if (pathSplit.Length > 2 && pathSplit[0] == "search" && pathSplit[1] == "q")
            {
                string[] parts = new string[pathSplit.Length - 1];
                for (int i = 1; i < pathSplit.Length; ++i)
                    parts[i-1] = pathSplit[i];

                app.Context.Items.Add("OriginalUrl", path);
                app.Context.RewritePath("~/search.aspx?params=" + parts.Concatenate('/'));

                return;
            }

            // Check if there's a route specified
            Route thisRoute = Routes.Current.GetRoute(path);
            if (thisRoute != null)
            {
                app.Context.Items.Add("OriginalUrl", path);
                app.Context.RewritePath(thisRoute.Filename + GetRouteVariables(app, thisRoute, path.Replace(".aspx", "")), false);

                return;
            }

            // The system will use the path for a direct file - if the file doesn't exist, redirect to the home page
            //if (!File.Exists(app.Server.MapPath(path)))
            //    Useful.RedirectTemporary("/");
        }

        protected string PageInCMS(HttpApplication app, string path)
        {
            string returnVal = string.Empty;
            string[] parts = path.Split('/');

            if (parts.Length > 2)
            {
                for (int i = 0; i < parts.Length; ++i)
                    parts[i] = app.Server.UrlDecode(parts[i]);

                path = parts.Concatenate('/').Replace("Default.aspx", "").Replace("default.aspx", "");

                string name = parts[parts.Length - 2];

                using (EuphoricNationEntities context = new EuphoricNationEntities())
                {
                    if (context.Contents.OfType<CMS>().Where(c => c.Path == path).Count() > 0)
                        returnVal = "~/Templates/CMS/CMS.aspx?name=" + name;
                }
            }

            return returnVal;
        }

        protected void context_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;

            if ((app.Context.CurrentHandler is System.Web.UI.Page) && app.Context.CurrentHandler != null)
            {
                System.Web.UI.Page pg = (System.Web.UI.Page)app.Context.CurrentHandler;
                pg.PreInit += new EventHandler(Page_PreInit);
            }
        }

        protected void Page_PreInit(object sender, EventArgs e)
        {
            System.Web.UI.Page page = (System.Web.UI.Page)sender;
            // restore internal path to original
            // this is required to handle postbacks

            if (HttpContext.Current.Items.Contains("OriginalUrl"))
            {
                string path = (string)HttpContext.Current.Items["OriginalUrl"];

                // save query string parameters to context
                RewriteContext con = new RewriteContext(page.Request.QueryString, path);
                HttpContext.Current.Items["RewriteContextInfo"] = con;

                if (path.IndexOf("?") == -1)
                    path += "?";

                HttpContext.Current.RewritePath(path);
            }
        }
    }
}